<html>
    <head>
        <style>
            .line {
                display: block;
                padding: 0 10px;
                line-height: 20px;
                min-height: 20px;
                white-space: pre;
                word-wrap: normal;
                font-family: consolas, Courier, "MS Courier New", monospace;
                font-size: 12px !important;
                color: #333;
                tab-size: 4;
            }
            /* Keywords: '<span class="k">{words}</span>',
            Punctuator: '<span class="p">{words}</span>',
            Identifer: '<span class="i">{words}</span>',
            StringLiteral : '<span class="s">{words}</span>',
            NumericLiteral : '<span class="n">{words}</span>',
            Comments : '<span class="c">{words}</span>',
            Others : '<span class="o">{words}</span>', */
            .k {font-weight: bold;color:#af00db}
            .i {color: #001080}
            .p {color: #000000}
            .s {color: #a31515}
            .n {color: #098658}

        </style>
    </head>
    <body>
        <pre>
            <script>
            class XRegExp{
                    constructor(source,flag,root="root"){
                        this.table=new Map();
                        this.regexp= new RegExp(this.compileRegExp(source, root, 0).source,flag); 
                        console.log(this.regexp); 
                        console.log(this.table);
                    }
            
                    compileRegExp(source,name,start){
                        if(source[name] instanceof RegExp){
                            return {
                                source: source[name].source, 
                                length:0
                            };
                        }
        
                        let length =0;
                        let regexp= source[name].replace(/\<([^>]+)\>/g, (str, $1)=>{
                            this.table.set(start + length, $1);
                            //this.table.set($1, start + length);
                            ++length;
                            let r= this.compileRegExp(source, $1, start + length);
                            length += r.length;
                            return "("+ r.source +")";
                        });
        
                        return {
                            source: regexp, 
                            length:length
                        }
                    }
                    
                    exec(string){
                        let r= this.regexp.exec(string);
                        for(let i=1; i<r.length; i++){
                            if(r[i] !== void 0){ // undefined
                                r[this.table.get(i-1)]=r[i];
                                r['tag'] = this.table.get(i-1);
                            }
                        }
                        
                        // console.log(JSON.stringify(r[0])); 
        
                        return r;
                    }
                    get lastIndex() {
                        return this.regexp.lastIndex;
                    }
                    set lastIndex(value) {
                        return this.regexp.lastIndex = value;
                    }
            }
            function scan(str){
                let regexp = new XRegExp({
                        InputElement:"<Whitespace>|<LineTerminator>|<Comments>|<Token>",
                        Whitespace:/ /,
                        LineTerminator: /\n/,
                        Comments:/\/\*(?:[^*]|\*[^\/])*\*\/|\/\/[^\n]*/,
                        Token:"<Literal>|<Keywords>|<Identifer>|<Punctuator>",
                        Literal:"<NumericLiteral>|<BooleanLiteral>|<StringLiteral>|<NullLiteral>",
                        NumericLiteral:/(?:[1-9][0-9]*|0)(?:\.[0-9]*)?|\.[0-9]+/,
                        BooleanLiteral:/true|false/,
                        StringLiteral:/\"(?:[^\"\n]|\\[\s\S])*\"|\'(?:[^\'\n]|\\[\s\S])*/,
                        NullLiteral:/null/,
                        Identifer:/[a-zA-Z_$1][a-zA-Z0-9_$]*/,
                        Keywords:/if|else|for|function|let|var/,
                        Punctuator:/\+|\-|\,|\?|\:|\{|\}|\(|\)|\.|\=|\<|\>|\+\+|\-\-|\=\=\=|\=\=|\=\>|\<\=|\*|\[|\]|;/
                    }, "g", "InputElement");
        
                    //let re regexp.exec("(a)");//console.1og(r);
                let first = true;
                while(regexp.lastIndex < str.length){
                    let r= regexp.exec(str);
                    console.log(r);
                    formatColorCode(r, first);
                    first = false;
                    if(!r[0].length)
                        break;
                }
            }

            // 简单的格式化颜色
            function formatColorCode(regResult, isNewLine = false)
            {
                const mapColor = {
                    Keywords: '<span class="k">{words}</span>',
                    Punctuator: '<span class="p">{words}</span>',
                    Identifer: '<span class="i">{words}</span>',
                    StringLiteral : '<span class="s">{words}</span>',
                    NumericLiteral : '<span class="n">{words}</span>',
                    Comments : '<span class="c">{words}</span>',
                    Others : '<span class="o">{words}</span>',
                }

                if(isNewLine){
                    document.write('<div class="line">');
                }

                if(regResult['tag'] === 'LineTerminator') {
                    document.write('</div>');
                    document.write('<div class="line">');
                    return;
                } 

                if(mapColor[regResult['tag']] !== undefined){
                    let words = mapColor[regResult['tag']].replace('{words}', regResult[0]);
                    document.write(words);
                } else {
                    let words = mapColor['Others'].replace('{words}', regResult[0]);
                    document.write(words);
                }

                
            }
            
            scan(`
                for(let i=0;i<3;i+t){
                    for(let j=0;j<3;j++){
                        let cell = document.createElement("div"); 
                        cell.classList.add("celI");
                        cell,innerText = pattern[i * 3 + j] == 2 ? "❌" : 
                            pattern[i * 3 + j] = 1 ? "⭕️" : "";	
                        cell.addEventListener("click", ()=>userMove(j,1));
                        board.appendChild(cel1);
                    }
                    board.appendChild(document.createElement("br"))
                }
            `)
            </script>
        </pre>
    </body>
</html>
